package com.iflytek.autofly.setting.biz.networking;

import android.content.Context;
import android.net.NetworkInfo.DetailedState;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;

import com.iflytek.autofly.utils.FlyLog;

/**
 * <b>Description: </b>wlan的每个accesspoint<br/>
 * <b>Change History:</b><br/>
 * <table border="1">
 * <tr>
 * <td><b>user</b></td>
 * <td><b>date</b></td>
 * <td><b>time</b></td>
 * <td><b>reason</b></td>
 * </tr>
 * <tr>
 * <td>xjzhao@iflytek.com</td>
 * <td>2015年5月26日</td>
 * <td>上午10:19:05</td>
 * <td>创建此文件</td>
 * </tr>
 * </table>
 *
 * @author xjzhao@iflytek.com
 */
public class AccessPoint implements Comparable<AccessPoint> {
    public static final int INVALID_NETWORK_ID = -1;
    /**
     * These values are matched in string arrays -- changes must be kept in sync
     */
    static final int SECURITY_NONE = 0;
    static final int SECURITY_WEP = 1;
    static final int SECURITY_PSK = 2;
    static final int SECURITY_EAP = 3;
    private static final String TAG = "AccessPoint";
    private final Context mContext;
    String ssid;
    String bssid;
    int security;
    boolean wpsAvailable = false;
    PskType pskType = PskType.UNKNOWN;
    /* package */ ScanResult mScanResult;
    DetailedState mState;
    private boolean flyChecked;
    private FlyState flyState;
    private WifiConfiguration mConfig;
    private int mRssi;
    private WifiInfo mInfo;

    public AccessPoint(Context context, WifiConfiguration config) {
        mContext = context;
        loadConfig(config);
        updateFlyState();
    }

    AccessPoint(Context context, ScanResult result) {
        mContext = context;
        loadResult(result);
        updateFlyState();
    }
//
//    public static WifiConfiguration getConfig(AccessPoint accessPoint) {
//        WifiConfiguration config = new WifiConfiguration();
//
//        // TODO
////        if (accessPoint == null) {
////            config.SSID = AccessPoint.convertToQuotedString(accessPoint.ssid);
////            config.hiddenSSID = true;
////        } else if (accessPoint.mNetworkId == INVALID_NETWORK_ID) {
////            config.SSID = AccessPoint.convertToQuotedString(accessPoint.ssid);
////        } else {
////            config.networkId = accessPoint.mNetworkId;
////        }
////        switch (accessPoint.security) {
////            case AccessPoint.SECURITY_NONE:
////                config.allowedKeyManagement.set(KeyMgmt.NONE);
////                break;
////
////            case AccessPoint.SECURITY_WEP:
////                config.allowedKeyManagement.set(KeyMgmt.NONE);
////                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
////                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
////                if (mPassword.length() != 0) {
////                    int length = mPassword.length();
////                    String password = mPassword.getText().toString();
////                    // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
////                    if ((length == 10 || length == 26 || length == 58) && password.matches("[0-9A-Fa-f]*")) {
////                        config.wepKeys[0] = password;
////                    } else {
////                        config.wepKeys[0] = '"' + password + '"';
////                    }
////                }
////                break;
////
////            case AccessPoint.SECURITY_PSK:
////                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
////                if (mPassword.length() != 0) {
////                    String password = mPassword.getText().toString();
////                    if (password.matches("[0-9A-Fa-f]{64}")) {
////                        config.preSharedKey = password;
////                    } else {
////                        config.preSharedKey = '"' + password + '"';
////                    }
////                }
////                break;
////
////            case AccessPoint.SECURITY_EAP:
////                config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
////                config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
////
////                // if (mPassword.isShown()) {
////                // // For security reasons, a previous password is not displayed to
////                // // user.
////                // // Update only if it has been changed.
////                // // if (mPassword.length() > 0) {
////                // // }
////                // }
////                break;
////            default:
////                return null;
////        }
//
//        return config;
//    }

    public static int getSecurity(WifiConfiguration config) {
        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
            return SECURITY_PSK;
        }
        if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
            return SECURITY_EAP;
        }
        return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
    }

    static String removeDoubleQuotes(String string) {
        int length = string.length();
        if ((length > 1) && (string.charAt(0) == '"')
                && (string.charAt(length - 1) == '"')) {
            return string.substring(1, length - 1);
        }
        return string;
    }

    static String convertToQuotedString(String string) {
        return "\"" + string + "\"";
    }

    private static int getSecurity(ScanResult result) {
        if (result.capabilities.contains("WEP")) {
            return SECURITY_WEP;
        } else if (result.capabilities.contains("PSK")) {
            return SECURITY_PSK;
        } else if (result.capabilities.contains("EAP")) {
            return SECURITY_EAP;
        }
        return SECURITY_NONE;
    }

    private static PskType getPskType(ScanResult result) {
        boolean wpa = result.capabilities.contains("WPA-PSK");
        boolean wpa2 = result.capabilities.contains("WPA2-PSK");
        if (wpa2 && wpa) {
            return PskType.WPA_WPA2;
        } else if (wpa2) {
            return PskType.WPA2;
        } else if (wpa) {
            return PskType.WPA;
        } else {
            Log.w(TAG, "Received abnormal flag string: " + result.capabilities);
            return PskType.UNKNOWN;
        }
    }

    public boolean isFlyChecked() {
        return flyChecked;
    }

    public void setFlyChecked(boolean flyChecked) {
        this.flyChecked = flyChecked;
    }

    public FlyState getFlyState() {
        return flyState;
    }

    public void setFlyState(FlyState flyState) {
        this.flyState = flyState;
    }

    public int getNetWorkId() {
        if (mConfig != null) {
            return mConfig.networkId;
        }
        return INVALID_NETWORK_ID;
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof AccessPoint)) {
            return false;
        }
        return (this.compareTo((AccessPoint) other) == 0);
    }

    @Override
    public int hashCode() {
        int result = 0;
        if (mInfo != null) {
            result += 13 * mInfo.hashCode();
        }
        result += 19 * mRssi;
        if (mConfig != null) {
            result += 23 * mConfig.networkId;
        } else {
            result += 23 * INVALID_NETWORK_ID;

        }
        result += 29 * ssid.hashCode();
        return result;
    }

    @Override
    public int compareTo(AccessPoint another) {
        // Active one goes first.
        if (mState != null && mState == DetailedState.CONNECTED) {
            return -1;
        }
        if (mInfo != null && another.mInfo == null) {
            return -1;
        }
        if (mInfo == null && another.mInfo != null) {
            return 1;
        }

        // Reachable one goes before unreachable one.
        if (mRssi != Integer.MAX_VALUE && another.mRssi == Integer.MAX_VALUE) {
            return -1;
        }
        if (mRssi == Integer.MAX_VALUE && another.mRssi != Integer.MAX_VALUE) {
            return 1;
        }

        // Configured one goes before unconfigured one.
        if (getNetWorkId() != INVALID_NETWORK_ID && another.getNetWorkId() == INVALID_NETWORK_ID) {
            return -1;
        }
        if (getNetWorkId() == INVALID_NETWORK_ID && another.getNetWorkId() != INVALID_NETWORK_ID) {
            return 1;
        }

        // Sort by signal strength.
        int difference = WifiManager.compareSignalLevel(another.mRssi, mRssi);
        if (difference != 0) {
            return difference;
        }
        // Sort by ssid.
        return ssid.compareToIgnoreCase(another.ssid);
    }

    public int getLevel() {
        if (mRssi == Integer.MAX_VALUE) {
            return -1;
        }
        return WifiManager.calculateSignalLevel(mRssi, 4);
    }

    private void loadConfig(WifiConfiguration config) {
        ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
        bssid = config.BSSID;
        security = getSecurity(config);
        if (security == SECURITY_PSK) {
        }
        mRssi = Integer.MAX_VALUE;
        mConfig = config;
        updateFlyState();
    }

    private void loadResult(ScanResult result) {
        ssid = result.SSID;
        bssid = result.BSSID;
        security = getSecurity(result);
        wpsAvailable = security != SECURITY_EAP && result.capabilities.contains("WPS");
        if (security == SECURITY_PSK)
            pskType = getPskType(result);
        mRssi = result.level;
        mScanResult = result;
        updateFlyState();
    }

    boolean update(ScanResult result) {
        if (ssid.equals(result.SSID) && security == getSecurity(result)) {
            if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) {
                mRssi = result.level;
            }
            // This flag only comes from scans, is not easily saved in config
            if (security == SECURITY_PSK) {
                pskType = getPskType(result);
            }
            return true;
        }
        return false;
    }

    void update(WifiInfo info, DetailedState state) {
        boolean reorder = false;
        if (info != null && getNetWorkId() != INVALID_NETWORK_ID
                && mConfig.networkId == info.getNetworkId()) {
            reorder = (mInfo == null);
            mRssi = info.getRssi();
            mInfo = info;
            mState = state;
            updateFlyState();
        } else if (mInfo != null) {
            reorder = true;
            mInfo = null;
            mState = null;
            updateFlyState();
        }
//        if (reorder) {
//            notifyHierarchyChanged();
//        }
    }

    private void updateFlyState() {
        if (mState != null && mState == DetailedState.CONNECTED) {
            flyState = FlyState.CONNECTED;
            FlyLog.d(TAG, "1 " + flyState);
            return;
        }

        if (mConfig != null
                && mConfig.status == WifiConfiguration.Status.DISABLED) {
            // saved, unconnect
            switch (mConfig.disableReason) {
                case -1:
                    break;
                case 3:
                    flyState = FlyState.VERIFY_FAILED;
                    FlyLog.d(TAG, "2 " + flyState);
                    return;
                default:
                    flyState = FlyState.SAVED;
                    FlyLog.d(TAG, "3 " + flyState);
                    return;
            }
        }
        if (mState != null) {
            switch (mState) {
                case AUTHENTICATING:
                case CONNECTING:
                case OBTAINING_IPADDR:
                case VERIFYING_POOR_LINK:
                    flyState = FlyState.CONNECTING;
                    FlyLog.d(TAG, "4 " + flyState);
                    return;
                case CONNECTED:
                    flyState = FlyState.CONNECTED;
                    FlyLog.d(TAG, "5 " + flyState);
                    return;
                case BLOCKED:
                case FAILED:
                    flyState = FlyState.VERIFY_FAILED;
                    FlyLog.d(TAG, "6 " + flyState);
                    return;
                case DISCONNECTED:
                case DISCONNECTING:
                case IDLE:
                case SCANNING:
                case SUSPENDED:
                    flyState = FlyState.UNCONNECTED;
                    FlyLog.d(TAG, "7 " + flyState);
                    return;
                default:
            }
            flyState = FlyState.UNCONNECTED;
            FlyLog.d(TAG, "8 " + flyState);
            return;
        }
        if (getNetWorkId() != INVALID_NETWORK_ID) {
            // saved, and unconected
            flyState = FlyState.SAVED;
            FlyLog.d(TAG, "9 " + flyState);
            return;
        }
        flyState = FlyState.UNCONNECTED;
        FlyLog.d(TAG, "10 " + flyState);
    }

    public WifiConfiguration getConfig() {
        return mConfig;
    }

    public void setConfig(WifiConfiguration config) {
        loadConfig(config);
        updateFlyState();
    }

    public int getSecurity() {
        return security;
    }

    enum FlyState {
        CONNECTING,
        CONNECTED,
        VERIFY_FAILED,
        UNCONNECTED,
        SAVED;

        @Override
        public String toString() {
            switch (this) {
                case CONNECTING:
                    return "CONNECTING";
                case CONNECTED:
                    return "CONNECTED";
                case VERIFY_FAILED:
                    return "VERIFY_FAILED";
                case UNCONNECTED:
                    return "UNCONNECTED";
                case SAVED:
                    return "SAVED";
            }
            return "invalid";
        }
    }

    enum PskType {
        UNKNOWN,
        WPA,
        WPA2,
        WPA_WPA2;

        @Override
        public String toString() {
            switch (this) {
                case UNKNOWN:
                    return "UNKNOWN";
                case WPA:
                    return "WPA";
                case WPA2:
                    return "WPA2";
                case WPA_WPA2:
                    return "WPA_WPA2";
            }
            return "invalid";
        }

    }

}
